/*
 * 程序名：demo06.cpp，此程序用于演示socket通信的服务端
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
using namespace std;
 
int main(int argc,char *argv[])
{
    if (argc!=2)
    {
        cout << "Using:./demo6 通讯端口\nExample:./demo6 5005\n\n";   // 端口大于1024，不与其它的重复。
        cout << "注意：运行服务端程序的Linux系统的防火墙必须要开通5005端口。\n";
        cout << "      如果是云服务器，还要开通云平台的访问策略。\n\n";
        return -1;
    }

    // 第1步：创建服务端的socket。 
    int listenfd = socket(AF_INET,SOCK_STREAM,0);
    if (listenfd==-1) 
    { 
        perror("socket"); 
        return -1; 
    }

    // 第2步：把服务端用于通信的IP和端口绑定到socket上。 
    struct sockaddr_in servaddr;                // 用于存放协议、端口和IP地址的结构体。
    memset(&servaddr,0,sizeof(servaddr));
    servaddr.sin_family=AF_INET;                // ①协议族，固定填AF_INET。
    servaddr.sin_port=htons(atoi(argv[1]));     // ②指定服务端的通信端口。
    servaddr.sin_addr.s_addr=htonl(INADDR_ANY); // ③如果操作系统有多个IP，全部的IP都可以用于通讯。
    //servaddr.sin_addr.s_addr=inet_addr("192.168.101.138"); // ③指定服务端用于通讯的IP(大端序)。
    // 绑定服务端的IP和端口。
    if (bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr))==-1)
    { 
        perror("bind"); 
        close(listenfd); 
        return -1; 
    }

    // 第3步：把socket设置为可连接（监听）的状态。
    if (listen(listenfd,5) == -1 ) 
    { 
        perror("listen"); 
        close(listenfd); 
        return -1; 
    }

    // 第4步：受理客户端的连接请求，如果没有客户端连上来，accept()函数将阻塞等待。
    int clientfd=accept(listenfd,0,0);
    if (clientfd==-1)
    {
        perror("accept"); 
        close(listenfd); 
        return -1; 
    }

    cout << "客户端已连接。\n";

    // 第5步：与客户端通信，接收客户端发过来的报文后，回复ok。
    char buffer[1024];
    while (true)
    {
        int iret;
        memset(buffer,0,sizeof(buffer));
        // 接收客户端的请求报文，如果客户端没有发送请求报文，recv()函数将阻塞等待。
        // 如果客户端已断开连接，recv()函数将返回0。
        if ( (iret=recv(clientfd,buffer,sizeof(buffer),0))<=0) 
        {
            cout << "iret=" << iret << endl;  break;   
        }
        cout << "接收：" << buffer << endl;

        strcpy(buffer,"ok");  // 生成回应报文内容。
        // 向客户端发送回应报文。
        if ( (iret=send(clientfd,buffer,strlen(buffer),0))<=0) 
        { 
            perror("send"); break; 
        }
        cout << "发送：" << buffer << endl;
    }

    // 第6步：关闭socket，释放资源。
    close(listenfd);   // 关闭服务端用于监听的socket。
    close(clientfd);   // 关闭客户端连上来的socket。
}